﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using Infinity.Common.Extensions;

namespace Infinity.Common
{
	public class SimpleCommandManager
	{
		protected class Rule
		{
			/// <summary>
			/// Required parameter count
			/// </summary>
			public int ParameterCount { get; set; }

			/// <summary>
			/// Command name
			/// </summary>
			public string Command { get; set; }

			public event Action<string[]> ParseEvent;

			/// <summary>
			/// It calls ParseEvent
			/// </summary>
			public void OnParseEvent(params string[] arguments)
			{
				if (ParseEvent != null)
					ParseEvent(arguments);
			}
		}

		/// <summary>
		/// Dictionary of rules
		/// </summary>
		protected Dictionary<string, Dictionary<int, Rule>> Rules { get; set; }

		/// <summary>
		/// Default constructor
		/// </summary>
		public SimpleCommandManager()
		{
			Rules = new Dictionary<string, Dictionary<int, Rule>>();
		}

		/// <summary>
		/// It creates a new rule. Every rule has a command and count of parameter. You can add an event for rule.
		/// It occurs when a rule has been activated (run). 
		/// </summary>
		public SimpleCommandManager CreateRule(string command, int parameterCount, Action<string[]> parseEvent)
		{
			Ensure.NotBlank(command);
			Ensure.NotNull(Rules);
			Ensure.True(parameterCount >= -1);

			Dictionary<int, Rule> rules;

			if (!Rules.TryGetValue(command, out rules))
			{
				rules = new Dictionary<int, Rule>();
				Rules.Add(command, rules);
			}

			Rule rule;
			if (!rules.TryGetValue(parameterCount, out rule))
			{
				rule = new Rule
					       {
						       Command = command,
						       ParameterCount = parameterCount
					       };
				rules.Add(parameterCount, rule);
			}
			else
				Error.ThrowException("There is already this type of rule!");

			if (parseEvent != null)
				rule.ParseEvent += parseEvent;

			return this;
		}

		/// <summary>
		/// It removes rule from Manager
		/// </summary>
		public bool RemoveRule(string command, int parameterCount)
		{
			Ensure.NotBlank(command);
			Ensure.NotNull(Rules);
			Ensure.True(parameterCount >= -1);

			Dictionary<int, Rule> rules;
			if (Rules.TryGetValue(command, out rules))
				return rules.Remove(parameterCount);
			return false;
		}

		/// <summary>
		/// It returns rule commands.
		/// </summary>
		public string[] GetCommands()
		{
			return Rules.Keys.ToArray();
		}

		/// <summary>
		/// It parses given parameters.
		/// </summary>
		public void Parse(string parameters)
		{
			Ensure.NotBlank(parameters);
			Parse(parameters.Split(new string[]
				                       {
					                       " "
				                       }, StringSplitOptions.RemoveEmptyEntries));
		}

		/// <summary>
		/// It parses given parameters.
		/// </summary>
		public void Parse(params string[] arguments)
		{
			Ensure.NotNull(arguments);
			arguments.ToList().ForEach(x => Ensure.NotBlank(x));
			var args = arguments.Select(x => x.Trim()).ToArray();

			var list = new List<Tuple<string, List<string>>>();

			foreach (var argument in args)
			{
				if (list.Count == 0)
				{
					if (!Rules.ContainsKey(argument))
						Error.ThrowException(string.Format("Any argument: {0}", argument));
					list.Add(Tuple.Create(argument, new List<string>()));
				}
				else
				{
					if (!Rules.ContainsKey(argument))
						list.Last().Item2.Add(argument);
					else
						list.Add(Tuple.Create(argument, new List<string>()));
				}
			}

			foreach (var rule in list)
			{
				var ruleName = rule.Item1;
				var argumentList = rule.Item2;

				Rule result;
				if (!Rules[ruleName].TryGetValue(argumentList.Count, out result))
					if (!Rules[ruleName].TryGetValue(-1, out result))
						Error.ThrowException("Parse error!");

				result.OnParseEvent(argumentList.ToArray());
			}
		}
	}
}